home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 8 / QRZ Ham Radio Callsign Database - Volume 8.iso / pc / files / t_unix / j109lxa4.tar / nntpcli.c < prev    next >
C/C++ Source or Header  |  1994-06-04  |  20KB  |  763 lines

  1. /*
  2.  *    Client routines for Network News Tranfer Protocol ala RFC977
  3.  *
  4.  *    Copyright 1990 Anders Klemets - SM0RGV, All Rights Reserved.
  5.  *    Permission granted for non-commercial copying and use, provided
  6.  *    this notice is retained.
  7.  *
  8.  *    Changes copyright 1990 Bernie Roehl, All Rights Reserved.
  9.  *    Permission granted for non-commercial copying and use, provided
  10.  *    this notice is retained.
  11.  *
  12.  *  Revision history:
  13.  *
  14.  *     May 11, 1990 - br checked for invalid chars in news filenames
  15.  *
  16.  *     May 10, 1990 - br changed date stamp in 'From ' lines to
  17.  *            seconds since GMT (to make parsing and expiry easier)
  18.  *
  19.  *     May 9, 1990 - br added locking of nntp.dat and history files,
  20.  *            second parameter to NNTP DIR, fixed bug in updating of
  21.  *            nntp.dat
  22.  *
  23.  *     early May, 1990 -- br added NNTP TRACE, NNTP DIR,
  24.  *            server-specific newsgroups and connection windows,
  25.  *            locking of newsgroup files using mlock() and rmlock(),
  26.  *            date stamping of 'From ' lines, increased stack space,
  27.  *            updating of nntp.dat only on successful sessions.
  28.  *
  29.  *     July 19, 1990 pa0gri Delinted and cleaned up. (calls and includes)
  30.  *
  31.  */
  32. #include <stdio.h>
  33. #include <sys/types.h>
  34. #include <time.h>
  35. #include <sys/timeb.h>
  36. #include <ctype.h>
  37. #include <string.h>  /* for strchr() */
  38. #ifdef    __TURBOC__
  39. #include <dir.h>
  40. #endif
  41. #include "global.h"
  42. #include "config.h"
  43. #include "timer.h"
  44. #include "cmdparse.h"
  45. #include "commands.h"
  46. #include "socket.h"
  47. #include "usock.h"
  48. #include "netuser.h"
  49. #include "proc.h"
  50. #include "smtp.h"
  51. #include "mailutil.h"
  52. #include "files.h"
  53.  
  54. #ifdef    NNTP
  55. #define NNTPMAXLEN    512
  56.  
  57. struct nntpservers {
  58.     struct timer nntpcli_t;
  59.     char *name;
  60.     char *groups;
  61.     int lowtime, hightime;  /* for connect window */
  62.     struct nntpservers *next;
  63. };
  64.  
  65. #define    NULLNNTP    (struct nntpservers *)NULL
  66.  
  67. #define MAXGROUPDIRS 10
  68.  
  69. static struct grouploc {
  70.     char *prefix;        /* e.g. comp, rec, net, talk, alt ... */
  71.     char *directory;     /* directory where these groups should be */
  72.     } groupdirs[MAXGROUPDIRS] = { NULL, NULL };
  73.  
  74. static struct nntpservers *Nntpservers = NULLNNTP;
  75. static char *Nntpgroups = NULLCHAR;
  76. static unsigned short nntptrace = 1;
  77. static int nntpquiet = 0;
  78. static char *News_spool = NULL;
  79. static int np_all = 0;  /* non-zero if Newsdir is a malloc'ed space */
  80.  
  81. static char *validchars = "abcdefghijklmnopqrstuvwxyz0123456789-_";
  82.  
  83. static void nntptick __ARGS((void *tp));
  84. static void nntp_job __ARGS((int i1,void *tp,void *v1));
  85. static int gettxt __ARGS((int s,FILE *fp));
  86. static int getreply __ARGS((int s));
  87. static int getarticle __ARGS((int s,char *msgid));
  88. static int dogroups __ARGS((int argc,char *argv[],void *p));
  89. static int doadds __ARGS((int argc,char *argv[],void *p));
  90. static int dodrops __ARGS((int argc,char *argv[],void *p));
  91. static int dokicks __ARGS((int argc,char *argv[],void *p));
  92. static int dolists __ARGS((int argc,char *argv[],void *p));
  93. static int donntrace __ARGS((int argc,char *argv[],void *p));
  94. static int donnquiet __ARGS((int argc,char *argv[],void *p));
  95. static int dondir __ARGS((int argc,char *argv[],void *p));
  96.  
  97. /* Tracing levels:
  98.     0 - no tracing
  99.     1 - serious errors reported
  100.     2 - transient errors reported
  101.     3 - session progress reported
  102.     4 - actual received articles displayed
  103.  */
  104.  
  105. static struct cmds Nntpcmds[] = {
  106.     "addserver",    doadds,    0,    3,
  107.     "nntp addserver <nntpserver> <interval>",
  108.     "directory",    dondir,    0,    0,    NULLCHAR,
  109.     "dropserver",    dodrops,    0,    2,
  110.     "nntp dropserver <nntpserver>",
  111.     "groups",    dogroups,    0,    0,    NULLCHAR,
  112.     "kick",        dokicks,    0,    2,
  113.     "nntp kick <nntpserver>",
  114.     "listservers",    dolists,    0,    0,    NULLCHAR,
  115.     "quiet",    donnquiet,    0,    0,    NULLCHAR,
  116.     "trace",    donntrace,    0,    0,    NULLCHAR,
  117.     NULLCHAR,
  118. };
  119.  
  120. int
  121. donntp(argc,argv,p)
  122. int argc;
  123. char *argv[];
  124. void *p;
  125. {
  126.     return subcmd(Nntpcmds,argc,argv,p);
  127. }
  128.  
  129. static int
  130. doadds(argc,argv,p)
  131. int argc;
  132. char *argv[];
  133. void *p;
  134. {
  135.     struct nntpservers *np;
  136.     for(np = Nntpservers; np != NULLNNTP; np = np->next)
  137.         if(stricmp(np->name,argv[1]) == 0)
  138.             break;
  139.     if (np == NULLNNTP) {
  140.         np = (struct nntpservers *) callocw(1,sizeof(struct nntpservers));
  141.         np->name = strdup(argv[1]);
  142.         np->next = Nntpservers;
  143.         Nntpservers = np;
  144.         np->groups = NULLCHAR;
  145.         np->lowtime = np->hightime = -1;
  146.         np->nntpcli_t.func = nntptick;    /* what to call on timeout */
  147.         np->nntpcli_t.arg = (void *)np;
  148.     }
  149.     if (argc > 3) {
  150.         int i;
  151.         if (np->groups == NULLCHAR) {
  152.             np->groups = mallocw(NNTPMAXLEN);
  153.             *np->groups = '\0';
  154.         }
  155.         for (i = 3; i < argc; ++i) {
  156.             if (isdigit(*argv[i])) {
  157.                 int lh, ll, hh, hl;
  158.                 sscanf(argv[i], "%d:%d-%d:%d", &lh, &ll, &hh, &hl);
  159.                 np->lowtime = lh * 100 + ll;
  160.                 np->hightime = hh * 100 + hl;
  161.             } else if ((strlen(np->groups)+strlen(argv[i])+2) >= NNTPMAXLEN)
  162.                 tprintf("Group list too long!  Group '%s' ignored!\n", argv[i]);
  163.             else {  /* it's a group, and it fits... add it to list */
  164.                 if (*np->groups != '\0')
  165.                     strcat(np->groups, ",");
  166.                 strcat(np->groups, argv[i]);
  167.             }
  168.         }
  169.         if (*np->groups == '\0') {    /* No groups specified? */
  170.             free(np->groups);
  171.             np->groups = NULLCHAR;
  172.         }
  173.     }
  174.     /* set timer duration */
  175.     set_timer(&np->nntpcli_t,atol(argv[2])*1000L);
  176.     start_timer(&np->nntpcli_t);        /* and fire it up */
  177.     return 0;
  178. }
  179.  
  180. static int
  181. dodrops(argc,argv,p)
  182. int argc;
  183. char *argv[];
  184. void *p;
  185. {
  186.     struct nntpservers *np, *npprev = NULLNNTP;
  187.     for(np = Nntpservers; np != NULLNNTP; npprev = np, np = np->next)
  188.         if(stricmp(np->name,argv[1]) == 0) {
  189.             stop_timer(&np->nntpcli_t);
  190.             free(np->name);
  191.             if (np->groups)
  192.                 free(np->groups);
  193.             if(npprev != NULLNNTP)
  194.                 npprev->next = np->next;
  195.             else
  196.                 Nntpservers = np->next;
  197.             free((char *)np);
  198.             return 0;
  199.     }
  200.     tprintf("No such server enabled.\n");
  201.     return 0;
  202. }
  203.  
  204. static int
  205. dolists(argc,argv,p)
  206. int argc;
  207. char *argv[];
  208. void *p;
  209. {
  210.     struct nntpservers *np;
  211.     for(np = Nntpservers; np != NULLNNTP; np = np->next) {
  212.         char tbuf[80];
  213.         if (np->lowtime != -1 && np->hightime != -1)
  214.             sprintf(tbuf, " -- %02d:%02d-%02d:%02d", np->lowtime/100, np->lowtime%100, np->hightime/100, np->hightime%100);
  215.         else
  216.             tbuf[0] = '\0';
  217.         tprintf("%-32s (%lu/%lu%s) %s\n", np->name,
  218.             read_timer(&np->nntpcli_t) /1000L,
  219.             dur_timer(&np->nntpcli_t) /1000L,
  220.             tbuf, np->groups ? np->groups : "");
  221.     }
  222.     return 0;
  223. }
  224.  
  225. static int donntrace(argc, argv, p)
  226. int argc;
  227. char *argv[];
  228. void *p;
  229. {
  230.     return setshort(&nntptrace,"NNTP tracing",argc,argv);
  231. }
  232.     
  233. static int donnquiet(argc, argv, p)
  234. int argc;
  235. char *argv[];
  236. void *p;
  237. {
  238.     return setbool(&nntpquiet,"NNTP quiet",argc,argv);
  239. }
  240.     
  241. static int dondir(argc, argv, p)
  242. int argc;
  243. char *argv[];
  244. void *p;
  245. {
  246.     if (argc < 2) {
  247.         int i;
  248.         tprintf("spool: %s\n", News_spool ? News_spool : Mailspool);
  249.         tprintf("control: %s\n", Newsdir);
  250.         for (i = 0; i < MAXGROUPDIRS; ++i)
  251.             if (groupdirs[i].prefix)
  252.                 tprintf("%-10.10s %s\n", groupdirs[i].prefix, groupdirs[i].directory);
  253.     } else {
  254.         char *p;
  255.         if ((p = strchr(argv[1], '=')) != NULLCHAR) {  /* set a groupdir */
  256.             int i;
  257.             *p++ = '\0';
  258.             for (i = 0; i < MAXGROUPDIRS; ++i)
  259.                 if (groupdirs[i].prefix)
  260.                     if (!strnicmp(groupdirs[i].prefix, argv[1], strlen(argv[1]))) {
  261.                         if (groupdirs[i].directory) {
  262.                             free(groupdirs[i].directory);
  263.                             groupdirs[i].directory = NULLCHAR;
  264.                             }
  265.                         if (*p == '\0') {
  266.                             free(groupdirs[i].prefix);
  267.                             groupdirs[i].prefix = NULLCHAR;
  268.                         } else
  269.                             groupdirs[i].directory = strdup(p);
  270.                         return 0;
  271.                     }
  272.             if (*p == '\0')  /* trashing a group that's not there */
  273.                 return 0;
  274.             for (i = 0; i < MAXGROUPDIRS; ++i){
  275.                 if (groupdirs[i].prefix == NULLCHAR) {
  276.                     groupdirs[i].prefix = strdup(argv[1]);
  277.                     if (groupdirs[i].directory) {
  278.                         free(groupdirs[i].directory);
  279.                         groupdirs[i].directory = NULL;
  280.                     }
  281.                     groupdirs[i].directory = strdup(p);
  282.                     return 0;
  283.                 }
  284.             }
  285.             tprintf("Directory table full\n");
  286.         }
  287.         else {  /* no '=', so just set default */
  288.             if (News_spool)
  289.                 free(News_spool);
  290.             News_spool = strdup(argv[1]);
  291.         }
  292.         if (argc > 2) {  /* they specified a newsdir as well */
  293.             if (np_all)
  294.                 free(Newsdir);
  295.             Newsdir = strdup(argv[2]);
  296.             np_all = 1;
  297.         }
  298.     }
  299.     return 0;
  300. }
  301.     
  302. static int
  303. dokicks(argc,argv,p)
  304. int argc;
  305. char *argv[];
  306. void *p;
  307. {
  308.     struct nntpservers *np;
  309.     for(np = Nntpservers; np != NULLNNTP; np = np->next)
  310.         if(stricmp(np->name,argv[1]) == 0) {
  311.             /* If the timer is not running, the timeout function has
  312.             * already been called and we don't want to call it again.
  313.             */
  314.             if(run_timer(&np->nntpcli_t)) {
  315.                 stop_timer(&np->nntpcli_t);
  316.             }
  317.             nntptick((void *)np);
  318.             return 0;
  319.     }
  320.     tprintf("No such server enabled.\n");
  321.     return 1;
  322. }
  323.  
  324. static int
  325. dogroups(argc,argv,p)
  326. int argc;
  327. char *argv[];
  328. void *p;
  329. {
  330.     int i;
  331.     if(argc < 2) {
  332.         if(Nntpgroups == NULLCHAR || (Nntpgroups != NULLCHAR && strcmp(Nntpgroups,"*") == 0))
  333.             tprintf("All groups are currently enabled.\n");
  334.         else
  335.             tprintf("Currently enabled newsgroups:\n%s\n",Nntpgroups);
  336.         return 0;
  337.     }
  338.     if(Nntpgroups == NULLCHAR)
  339.         Nntpgroups = mallocw(NNTPMAXLEN);
  340.     *Nntpgroups = '\0';
  341.     for(i=1; i < argc; ++i) {
  342.         if(i > 1)
  343.             strcat(Nntpgroups,",");
  344.         strcat(Nntpgroups,argv[i]);
  345.     }
  346.     return 0;
  347. }
  348.  
  349. /* This is the routine that gets called every so often to connect to
  350.  * NNTP servers.
  351.  */
  352. static void
  353. nntptick(tp)
  354. void *tp;
  355. {
  356.     newproc("NNTP client", 3072, nntp_job, 0, tp, NULL,0);
  357. }
  358.  
  359. static void
  360. nntp_job(i1,tp,v1)
  361. int i1;
  362. void *tp, *v1;
  363. {
  364.     FILE *fp, *tmpf;
  365.     int s = -1, i;
  366. /*    long pos; */
  367.     struct tm *ltm;
  368.     time_t t;
  369.     int now;
  370.     struct nntpservers *np = (struct nntpservers *) tp;
  371.     struct sockaddr_in fsocket;
  372.     char tbuf[NNTPMAXLEN], buf[NNTPMAXLEN], *cp, *lastdate = NULLCHAR;
  373.     if (nntptrace >= 3)
  374.         tprintf("NNTP daemon entered, target = %s\n",np->name);
  375. #ifndef UNIX
  376.     if(availmem() < Memthresh + 4000L){
  377.         if (nntptrace >= 2)
  378.             tprintf("NNTP daemon quit -- low memory\n");
  379.         /* Memory is tight, don't do anything */
  380.         start_timer(&np->nntpcli_t);
  381.         return;
  382.     }
  383. #endif
  384.  
  385.     time(&t);    /* more portable than gettime() */
  386.     ltm = localtime(&t);
  387.     now = ltm->tm_hour * 100 + ltm->tm_min;
  388.     if (np->lowtime < np->hightime) {  /* doesn't cross midnight */
  389.         if (now < np->lowtime || now >= np->hightime) {
  390.             if (nntptrace >= 3)
  391.                 tprintf("NNTP window to '%s' not open\n", np->name);
  392.             start_timer(&np->nntpcli_t);
  393.             return;
  394.         }
  395.     } else {
  396.         if (now < np->lowtime && now >= np->hightime) {
  397.             if (nntptrace >= 3)
  398.                 tprintf("NNTP window to '%s' not open\n", np->name);
  399.             start_timer(&np->nntpcli_t);
  400.             return;
  401.         }
  402.     }
  403.  
  404.     fsocket.sin_addr.s_addr = resolve(np->name);
  405.     if(fsocket.sin_addr.s_addr == 0) {  /* No IP address found */
  406.         if (nntptrace >= 2)
  407.             tprintf("NNTP can't resolve host '%s'\n", np->name);
  408.         /* Try again later */
  409.         start_timer(&np->nntpcli_t);
  410.         return;
  411.     }
  412.     fsocket.sin_family = AF_INET;
  413.     fsocket.sin_port = IPPORT_NNTP;
  414.  
  415.     s = socket(AF_INET,SOCK_STREAM,0);
  416.     sockmode(s,SOCK_ASCII);
  417.     if(connect(s,(char *)&fsocket,SOCKSIZE) == -1){
  418.         cp = sockerr(s);
  419.         log(s,"NNTP %s Connect failed: %s",psocket(&fsocket),
  420.             cp != NULLCHAR ? cp : "");
  421.         if (nntptrace >= 2)
  422.             tprintf("NNTP %s Connect failed: %s\n",psocket(&fsocket),
  423.         cp != NULLCHAR ? cp : "");
  424.         goto quit;
  425.     }
  426.     /* Eat the banner */
  427.     i = getreply(s);
  428.     if(i == -1 || i >= 400) {
  429.         log(s,"NNTP %s bad reply on banner (response was %d)",psocket(&fsocket),i);
  430.         if (nntptrace >= 1)
  431.             tprintf("NNTP %s bad reply on banner (response was %d)\n",psocket(&fsocket),i);
  432.         goto quit;
  433.     }
  434.  
  435.     if (mlock(Newsdir, "nntp")) {
  436.         if (nntptrace >= 2)
  437.             tprintf("NNTP %s Connect failed: cannot lock nntp.dat\n", psocket(&fsocket));
  438.         goto quit;
  439.     }
  440.     sprintf(buf,"%s/nntp.dat",Newsdir);
  441.     if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
  442.         log(s,"NNTP %s Connect failed: Cannot open %s",psocket(&fsocket),
  443.             buf);
  444.         if (nntptrace >= 1)
  445.             tprintf("NNTP %s Connect failed: Cannot open %s\n",psocket(&fsocket), buf);
  446.         rmlock(Newsdir, "nntp");
  447.         goto quit;
  448.     }
  449.     rewind(fp);
  450. /*    for(pos=0L; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;pos=ftell(fp)) { */
  451.     for(; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;) {
  452.         if((cp = strchr(buf,' ')) == NULLCHAR)
  453.             continue;    /* something wrong with this line, skip it */
  454.         *cp = '\0';
  455.         if(stricmp(buf,np->name) == 0) {
  456.             rip(cp+1);
  457.             lastdate = strdup(cp+1);
  458.             break;
  459.         }
  460.     }
  461.     fclose(fp);
  462.     rmlock(Newsdir, "nntp");
  463.  
  464.     if(lastdate == NULLCHAR)
  465.         lastdate = strdup("700101 000000");
  466.     /* snapshot the time for use later in re-writing nntp.dat */
  467.     time(&t);
  468.     ltm = localtime(&t);
  469.                 
  470.     /* Get a list of new message-id's */
  471.     if (np->groups) {
  472.         if (nntptrace >= 3)
  473.             tprintf("==>NEWNEWS %s %s\n", np->groups, lastdate);
  474.         usprintf(s,"NEWNEWS %s %s\n", np->groups, lastdate);
  475.     } else {
  476.         if (nntptrace >= 3)
  477.             tprintf("==>NEWNEWS %s %s\n", Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
  478.         usprintf(s,"NEWNEWS %s %s\n",Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
  479.     }
  480.     free(lastdate);
  481.     /* Get the response */
  482.     if((i = getreply(s)) != 230) { /* protocol error */
  483.         log(s,"NNTP %s protocol error (response was %d)",psocket(&fsocket),i);
  484.         if (nntptrace >= 1)
  485.             tprintf("NNTP %s protocol error (response was %d)\n",psocket(&fsocket),i);
  486.         goto quit;
  487.     }
  488.     if((tmpf = tmpfile()) == NULLFILE) {
  489.         if (nntptrace >= 1)
  490.             tprintf("NNTP %s Cannot open temp file\n", psocket(&fsocket));
  491.         goto quit;
  492.     }
  493.     if(gettxt(s,tmpf) == -1) {
  494.         log(s, "NNTP %s giving up: gettxt() failure",psocket(&fsocket));
  495.         if (nntptrace >= 1)
  496.             tprintf("NNTP %s giving up: gettxt() failure\n",psocket(&fsocket));
  497.         fclose(tmpf);
  498.         goto quit;
  499.     }
  500.  
  501.     /* Open the history file */
  502.     if (mlock(Newsdir, "history")) {
  503.         if (nntptrace >= 1)
  504.             tprintf("NNTP %s giving up: couldn't lock history file\n", psocket(&fsocket));
  505.         fclose(tmpf);
  506.         goto quit;
  507.     }
  508.     sprintf(buf,"%s/history",Newsdir);
  509.     if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
  510.         log(s,"NNTP %s Connect failed: Cannot open %s",psocket(&fsocket), buf);
  511.         if (nntptrace >= 1)
  512.             tprintf("NNTP %s Connect failed: Cannot open %s\n",psocket(&fsocket), buf);
  513.         fclose(tmpf);
  514.         goto quit;
  515.     }
  516.     /* search through the history file for matching message id's */
  517.     rewind(tmpf);
  518.     while(fgets(tbuf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  519.         i = 0;
  520.         rewind(fp);
  521.         while(fgets(buf,NNTPMAXLEN,fp) != NULLCHAR) {
  522.             if(stricmp(buf,tbuf) == 0) {
  523.                 i = 1;
  524.                 break;
  525.             }
  526.             pwait(NULL);
  527.         }
  528.         if(i == 0) {        /* not found, get the article */
  529.             if(getarticle(s,tbuf) == -1) {
  530.                 log(s,"NNTP %s Giving up: could not get article",psocket(&fsocket));
  531.                 if (nntptrace >= 2)
  532.                     tprintf("NNTP %s Giving up: could not get article\n",psocket(&fsocket));
  533.                 fclose(fp);
  534.                 rmlock(Newsdir, "history");
  535.                 fclose(tmpf);
  536.                 goto quit;
  537.             }
  538.             fprintf(fp,"%s",tbuf); /* add the new message id */
  539.         }
  540.     }
  541.     fclose(fp);
  542.     rmlock(Newsdir, "history");
  543.     fclose(tmpf);
  544.     if (nntptrace >= 3)
  545.         tprintf("==>QUIT\n");
  546.     usprintf(s,"QUIT\n");
  547.     /* Eat the response */
  548.     getreply(s);
  549.     /* NOW, update the nntp.dat file */
  550.     if (mlock(Newsdir, "nntp")) {
  551.         if (nntptrace >= 2)
  552.             tprintf("NNTP %s Could not lock nntp.dat for update\n", psocket(&fsocket));
  553.         goto quit;
  554.     }
  555.     sprintf(buf,"%s/nntp.dat",Newsdir);
  556.     fp = fopen(buf,READ_TEXT);
  557.     sprintf(buf, "%s/nntp.tmp",Newsdir);
  558.     if ((tmpf = fopen(buf, WRITE_TEXT)) == NULLFILE)
  559.         if (nntptrace >= 1)
  560.             tprintf("NNTP %s Cannot create temp file '%s'\n", psocket(&fsocket), buf);
  561.     if (fp == NULLFILE || tmpf == NULLFILE) {
  562.         log(s,"NNTP %s Could not update %s", psocket(&fsocket), buf);
  563.         if (nntptrace >= 2)
  564.             tprintf("NNTP %s Could not update %s\n",psocket(&fsocket), buf);
  565.         if (fp)
  566.             fclose(fp);
  567.         if (tmpf)
  568.             fclose(tmpf);
  569.         rmlock(Newsdir, "nntp");
  570.         goto quit;
  571.     }
  572.     while (fgets(tbuf, sizeof(tbuf), fp))
  573.         if (strnicmp(tbuf, np->name, strlen(np->name)))
  574.             fputs(tbuf, tmpf);
  575.     fprintf(tmpf,"%s %02d%02d%02d %02d%02d%02d\n",np->name,ltm->tm_year%100,ltm->tm_mon+1,
  576.         ltm->tm_mday,ltm->tm_hour,ltm->tm_min,ltm->tm_sec);
  577.     fclose(fp);
  578.     fclose(tmpf);
  579.     sprintf(buf, "%s/nntp.dat", Newsdir);
  580.     sprintf(tbuf, "%s/nntp.tmp", Newsdir);
  581.     unlink(buf);
  582.     rename(tbuf, buf);
  583.     rmlock(Newsdir, "nntp");
  584. quit:
  585.     if (nntptrace >= 3)
  586.         tprintf("NNTP daemon exiting\n");
  587.     close_s(s);
  588.     /* Restart timer */
  589.     start_timer(&np->nntpcli_t);
  590.     return;
  591. }
  592.  
  593. static int
  594. gettxt(s,fp)
  595. int s;
  596. FILE *fp;
  597. {
  598.     char buf[NNTPMAXLEN];
  599.     int nlines;
  600.     for (nlines = 0; recvline(s,buf,NNTPMAXLEN) != -1; ++nlines) {
  601.         if (nntptrace >= 4)
  602.             tprintf("<==%s", buf);
  603.         if(strcmp(buf,".\n") == 0) {
  604.             if (nntptrace >= 3)
  605.                 tprintf("NNTP received %d lines\n", nlines);
  606.             return 0;
  607.             }
  608.         /* check for escaped '.' characters */
  609.         if(strcmp(buf,"..\n") == 0)
  610.             fputs(".\n",fp);
  611.         else
  612.             fputs(buf,fp);
  613.     }
  614.     if (nntptrace >= 1)
  615.         tprintf("NNTP receive error after %d lines\n", nlines);
  616.     return -1;
  617. }
  618.  
  619. static int
  620. getreply(s)
  621. int s;
  622. {
  623.     char buf[NNTPMAXLEN];
  624.     int response;
  625.     while(recvline(s,buf,NNTPMAXLEN) != -1) {
  626.         /* skip informative messages and blank lines */
  627.         if(buf[0] == '\0' || buf[0] == '1')
  628.             continue;
  629.         sscanf(buf,"%d",&response);
  630.         if (nntptrace >= 3)
  631.             tprintf("<==%s\n", buf);
  632.         return response;
  633.     }
  634.     if (nntptrace >= 3)
  635.         tprintf("==No response\n");
  636.     return -1;
  637. }
  638.  
  639. static int
  640. getarticle(s,msgid)
  641. int s;
  642. char *msgid;
  643. {
  644.     char buf[NNTPMAXLEN], froml[NNTPMAXLEN], newgl[NNTPMAXLEN];
  645.     FILE *fp, *tmpf;
  646.     int r;
  647.     char *cp;
  648.     extern int Smtpquiet;
  649. #ifdef USERLOG
  650.     time_t t;
  651. #endif
  652.  
  653.     if (nntptrace >= 3)
  654.         tprintf("==>ARTICLE %s", msgid);
  655.     usprintf(s,"ARTICLE %s", msgid);
  656.     r = getreply(s);
  657.     if(r == -1 || r >= 500)
  658.         return -1;
  659.     if(r >= 400)
  660.         return 0;
  661.     if((tmpf = tmpfile()) == NULLFILE) {
  662.         if (nntptrace >= 1)
  663.             tprintf("NNTP Cannot open temp file for article\n");
  664.         return -1;
  665.     }
  666.     if(gettxt(s,tmpf) == -1) {
  667.         fclose(tmpf);
  668.         return -1;
  669.     }
  670.     /* convert the article into mail format */
  671.     rewind(tmpf);
  672.     froml[0] = '\0';
  673.     newgl[0] = '\0';
  674.     while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  675.         if(strncmp(buf,"From: ",6) == 0) {
  676.             struct timeb t;
  677.             ftime(&t);
  678.             rip(&buf[6]);
  679.             sprintf(froml,"From %s %ld\n",&buf[6], t.time);
  680.             if(newgl[0] != '\0')
  681.                 break;
  682.         }
  683.         if(strncmp(buf,"Newsgroups: ",12) == 0) {
  684.             strcpy(newgl,&buf[12]);
  685.             if(froml[0] != '\0')
  686.                 break;
  687.         }
  688.         /* invalid article - missing 'From:' line or 'Newsgroups:' line */
  689.         if(strcmp(buf,"\n") == 0 && (froml[0] == '\0' || newgl[0] == '\0')) {
  690. /*            fclose(fp); */
  691.             fclose(tmpf);
  692.             return 0;
  693.         }
  694.     }
  695.     sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
  696.     for(cp=newgl;;++cp) {
  697.         if(*cp == '.') {
  698. #ifdef __TURBOC__
  699.             mkdir(buf); /* create a subdirectory, if necessary */
  700. #else
  701.             mkdir(buf,0755); /* create a subdirectory, if necessary */
  702. #endif
  703.             strcat(buf,"/");
  704.             continue;
  705.         }
  706.         if(*cp == ',' || *cp == '\n') {
  707.             char tempdir[80], prefix[20], *p;
  708.             strcpy(tempdir, buf);
  709.             if ((p = strrchr(tempdir, '/')) != NULLCHAR) {
  710.                 *p++ = '\0';
  711.                 strcpy(prefix, p);
  712.             }
  713.             if (mlock(tempdir, prefix)) {
  714.                 if (nntptrace >= 2)
  715.                     tprintf("NNTP group '%s' is locked\n", buf);
  716.                 return -1;
  717.             }
  718.             strcat(buf,".txt");
  719.             /* open the mail file */
  720.             if (nntptrace >= 3)
  721.                 tprintf("Writing article to '%s'\n", buf);
  722.             if((fp = fopen(buf,APPEND_TEXT)) != NULLFILE) {
  723.                 fputs(froml,fp);
  724. #ifdef USERLOG
  725.                 /* If the userlog code is enabled, we need a
  726.                  * "Received: " line to get the message id
  727.                  * that is used in it - WG7J
  728.                  */
  729.                 time(&t);
  730.                 fprintf(fp,Hdrs[RECEIVED]);
  731.                 fprintf(fp,"by %s with NNTP\n\tid AA%ld ; %s",
  732.                         Hostname, get_msgid(),ptime(&t));
  733. #endif
  734.                 rewind(tmpf);
  735.                 while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  736.                     /* for UNIX mail compatiblity */
  737.                     if(strncmp(buf,"From ",5) == 0)
  738.                         putc('>',fp);
  739.                     fputs(buf,fp);
  740.                 }
  741.                 putc('\n',fp);
  742.                 fclose(fp);
  743.             }
  744.             rmlock(tempdir, prefix);
  745.             if (*cp == '\n') 
  746.                 break;
  747.             else
  748.                 sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
  749.             continue;
  750.         }
  751.         buf[strlen(buf)+1] = '\0';
  752.         buf[strlen(buf)] = strchr(validchars, tolower(*cp)) ? *cp : '_';
  753.     }
  754.     fclose(tmpf);
  755.     strcpy(buf,msgid);        /* Get a copy we can munge */
  756.     rip(buf);            /* remove trailing new-line */
  757.     rip(newgl);            /* ditto */
  758.     if(!nntpquiet)
  759.         tprintf("New news arrived: %s, article %s%c\n",newgl,buf,Smtpquiet?' ':'\007');
  760.     return 0;
  761. }
  762. #endif    /* NNTP */
  763.